/*
 * @Description  :
 * @Version      : 0.1
 * @Company      : V-Think Development Team
 * @Author       : KiraVerSace@yeah.net
 * @Date         : 2022-11-22 18:57:08
 * @LastEditTime : 2024-09-03 15:55:47
 */
#include <BootLoader.h>

OTAParameterT otaParameter;

typedef void (*pFunction)(void);

void bootLoaderInit(void)
{
    memset(&otaParameter, 0x00, sizeof(otaParameter));

    otaParameter.startRangeIndex = 0;
    otaParameter.endRangeIndex   = OTA_HTTP_GET_SIZE_BYTE_PER-1;

    otaParameter.firmwareRxCount = 0;

    __HAL_RCC_SYSCFG_CLK_ENABLE();
    __HAL_RCC_FLASH_CLK_ENABLE();

    /* Clear flash flags */
    HAL_FLASH_Unlock();
    __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS);
    HAL_FLASH_Lock();
}

/**
 * @brief  This function checks whether a valid application exists in flash.
 *         The check is performed by checking the very first DWORD (4 bytes) of
 *         the application firmware. In case of a valid application, this DWORD
 *         must represent the initialization location of stack pointer - which
 *         must be within the boundaries of RAM.
 */
bool bootLoaderCheckApplicationExist(void)
{
    /* Test if user code is programmed starting from address "APPLICATION_ADDRESS" */
    if (((*(__IO uint32_t*)USR_APP_ADDRESS) & 0x2FFE0000 ) == 0x20000000)
    {
        return true;
    }
    else
    {
        logError("No Application found!")
        return false;
    }

    // if (((*(uint32_t*)USR_APP_ADDRESS) - SRAM1_BASE) <= SRAM1_SIZE_MAX)
    // {
    //     logInfo("Application is exist.")
    //     return true;
    // }
    // else
    // {
    //     logError("No Application found!")
    //     return false;
    // }
}


/**
 * @brief  This function performs the jump to the user application in flash.
 * @details The function carries out the following operations:
 *  - De-initialize the clock and peripheral configuration
 *  - Stop the SysTick
 *  - Set the vector table location (if ::SET_VECTOR_TABLE is enabled)
 *  - Sets the stack pointer location
 *  - Perform the jump
 */
void bootLoaderJumpToApplication(void)
{
    uint32_t  jumpAddress       = *(__IO uint32_t*)(USR_APP_ADDRESS + 4);
    pFunction jumpToApplication = (pFunction)jumpAddress;

    __set_FAULTMASK(1);

    // HAL_DeInit();            // 加上以后程序不能跳转
    // HAL_RCC_DeInit();        // 加上以后程序不能跳转
    // noTone(BUZZER, true);    // 加上以后程序不能跳转

    SysTick->CTRL = 0;
    SysTick->LOAD = 0;
    SysTick->VAL  = 0;

    /**
     * @brief 如果使用了蜂鸣器,没有这个函数就会跳转失败
     *        用noTone会导致无法跳转
     */
    for (uint8_t i=0; i<8; i++)
    {
        NVIC->ICER[i] = 0xFFFFFFFF;
        NVIC->ICPR[i] = 0xFFFFFFFF;
    }

    __set_FAULTMASK(0);

    /* Initialize user application's Stack Pointer */
    __set_MSP(*(__IO uint32_t*)USR_APP_ADDRESS);
	jumpToApplication();
}

bool bootLoaderEraseFlash(void)
{
    /**
     * @brief 定义FLASH每个Bank有多少Pages
     *        STM32L4系列分为两个Bank,每个Bank=256Pages 每个Page=2K
     *        改用宏定义来定义一个Bank有多少页
     *        自带参数算出来不准(FLASH_BANK_SIZE/FLASH_PAGE_SIZE)
	 *        主要FLASH_BANK_SIZE参数有错误!!!
     */
    const uint16_t FLASH_PAGE_NUMBER_PER_BANK = 256;

    uint32_t numberOfPages = 0;
    uint32_t pageError = 0;

    FLASH_EraseInitTypeDef flashEraseInit;
    HAL_StatusTypeDef halStatus = HAL_OK;

    HAL_FLASH_Unlock();

    numberOfPages = (FLASH_BASE+FLASH_SIZE-USR_APP_ADDRESS)/FLASH_PAGE_SIZE;

    // logDebug("numberOfPages=[%d]", numberOfPages);
    // logDebug("NbPages=[%d]", numberOfPages % FLASH_PAGE_NUMBER_PER_BANK);
    // logDebug("Page=[%d]", (USR_APP_ADDRESS-FLASH_BASE)/FLASH_PAGE_SIZE);

    // logDebug("Erase BANK1....");    delay(100);

    flashEraseInit.Banks     = FLASH_BANK_1;                                        // 擦除指定的Bank 目前用的L476RC只有一个Bank
    flashEraseInit.NbPages   = numberOfPages % FLASH_PAGE_NUMBER_PER_BANK;          // 一次性擦除多少页
    flashEraseInit.Page      = (USR_APP_ADDRESS-FLASH_BASE)/FLASH_PAGE_SIZE;        // 擦除的起始页,从除去BootLoader开始
    flashEraseInit.TypeErase = FLASH_TYPEERASE_PAGES;                               // 擦除类型是“Page Erase”

    halStatus = HAL_FLASHEx_Erase(&flashEraseInit, &pageError);

    if (halStatus == HAL_OK)
    {
        // logDebug("Erase BANK2....");    delay(100);
        flashEraseInit.Banks     = FLASH_BANK_2;
        flashEraseInit.NbPages   = FLASH_PAGE_NUMBER_PER_BANK;                      // 减去上次擦除的Pages得到剩余的
        flashEraseInit.Page      = FLASH_PAGE_NUMBER_PER_BANK - flashEraseInit.NbPages;
        flashEraseInit.TypeErase = FLASH_TYPEERASE_PAGES;

        halStatus = HAL_FLASHEx_Erase(&flashEraseInit, &pageError);
    }
    HAL_FLASH_Lock();

    if (halStatus == HAL_OK)
    {
        logInfo("Erase the flash [0x%08X-0x%08X] successfully!", USR_APP_ADDRESS, USR_APP_ADDRESS+FLASH_SIZE);

        return true;
    }
    else
    {
        logError("Erase the flash error!");
        return false;
    }
}

bool bootLoaderWriteFlash(const uint32_t address, const uint8_t pValue[], const uint32_t length)
{
    // HAL_FLASH_Unlock();
    // for(uint32_t i=0; i<length; i+=8 )
    // {
    //     while( HAL_OK != HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, address+i, *(uint64_t *)(pValue+i) ) );
    // }
    // HAL_FLASH_Lock();

    if((address < FLASH_BASE) || (address >= (FLASH_BASE+FLASH_SIZE)) || (length <= 0))
    {
        return false;   // 非法地址
    }

    uint32_t sAddress = address;

    HAL_FLASH_Unlock();

    /* 此处修复了一个较大的BUG,在固件不能被8整除时候,会剩下4个字节不被写进去 */
    for (uint32_t i=0; ((i < (length+4)/8) && (sAddress <= (FLASH_BASE+FLASH_SIZE-8))); i++)
    {
        if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, sAddress, *((uint64_t *)(pValue+8*i))) == HAL_OK)
        {
            /* Check the written value */
            if (*(uint64_t*)sAddress != *(uint64_t *)(pValue+8*i))
            {
                /* Flash content doesn't match source content */
                HAL_FLASH_Lock();
                return false;
            }
            /* Increment FLASH destination address */
            sAddress += 8;
        }
        else
        {
            /* Error occurred while writing data in Flash memory */
            HAL_FLASH_Lock();
            return false;
        }
    }

    /* Lock the Flash to disable the flash control register access */
    HAL_FLASH_Lock();

    return true;
}

uint32_t HAL_BigEnd_CRC_Calculate(CRC_HandleTypeDef *hcrc, uint32_t pBuffer[],
                                  uint32_t BufferLength)
{
    uint32_t index;     /* CRC input data buffer index */
    uint32_t temp = 0U; /* CRC output (read from hcrc->Instance->DR register) */
    union LE2BE
    {
        uint32_t u32;
        uint8_t u8Arr[4];
    } le2be;

    /* Change CRC peripheral state */
    hcrc->State = HAL_CRC_STATE_BUSY;

    /* Reset CRC Calculation Unit (hcrc->Instance->INIT is
     *  written in hcrc->Instance->DR) */
    __HAL_CRC_DR_RESET(hcrc);

    switch (hcrc->InputDataFormat)
    {
    case CRC_INPUTDATA_FORMAT_WORDS:
        /* Enter 32-bit input data to the CRC calculator */
        for (index = 0U; index < BufferLength; index++)
        {
            // 通过联合体，将uint32_t 中的高低字节调换
            le2be.u32 = pBuffer[index];
            hcrc->Instance->DR = (le2be.u8Arr[0] << 24) | (le2be.u8Arr[1] << 16) | (le2be.u8Arr[2] << 8) | (le2be.u8Arr[3] << 0);
            // hcrc->Instance->DR = pBuffer[index];
        }
        temp = hcrc->Instance->DR;
        break;

    default:
        break;
    }

    /* Change CRC peripheral state */
    hcrc->State = HAL_CRC_STATE_READY;

    /* Return the CRC computed value */
    return temp;
}

uint32_t bootLoaderCRC32Verify(const uint32_t length)
{
    CRC_HandleTypeDef CrcHandle;
    volatile uint32_t calculatedCrc = 0;

    __HAL_RCC_CRC_CLK_ENABLE();
    CrcHandle.Instance                     = CRC;
    CrcHandle.Init.DefaultPolynomialUse    = DEFAULT_POLYNOMIAL_ENABLE;
    CrcHandle.Init.DefaultInitValueUse     = DEFAULT_INIT_VALUE_ENABLE;
    CrcHandle.Init.InputDataInversionMode  = CRC_INPUTDATA_INVERSION_NONE;
    CrcHandle.Init.OutputDataInversionMode = CRC_OUTPUTDATA_INVERSION_DISABLE;
    CrcHandle.InputDataFormat              = CRC_INPUTDATA_FORMAT_WORDS;
    if(HAL_CRC_Init(&CrcHandle) != HAL_OK)
    {
        return 0;
    }

    // calculatedCrc = HAL_CRC_Calculate(&CrcHandle, (uint32_t*)USR_APP_ADDRESS, length/4);
    calculatedCrc = HAL_BigEnd_CRC_Calculate(&CrcHandle, (uint32_t*)USR_APP_ADDRESS, length/4);

    __HAL_RCC_CRC_FORCE_RESET();
    __HAL_RCC_CRC_RELEASE_RESET();

    logDebug("Firmware CRC32=[0x%08X]", calculatedCrc);

    return calculatedCrc;
}

/**
 * @brief 将字符串"0x1234" "DEC"转成对应的数字
 */
// static uint32_t hexStr2Int(const char* str)
// {
// 	int n = 0;
// 	if (str == NULL) return 0;
// 	if (*str == '0' && (*(str + 1) == 'x' || *(str + 1) == 'X'))
// 	{
// 		str += 2;
// 	}
// 	while (1)
// 	{
// 		if (*str >= '0' && *str <= '9')
// 		{
// 			n = 16 * n + (*str - '0');
// 		}
// 		else if (*str >= 'A' && *str <= 'F')//十六进制还要判断字符是不是在A-F或者a-f之间
// 		{
// 			n = 16 * n + (*str - 'A' + 10);
// 		}
// 		else if (*str >= 'a' && *str <= 'f')
// 		{
// 			n = 16 * n + (*str - 'a' + 10);
// 		}
// 		else
// 		{
// 			break;
// 		}
// 		str++;
// 	}
// 	return n;
// }

// http://ota.iot-api.com/firmware/sh-4/mpdexekq/0nm447s1y1ylsaa0lhgip3mafm0t9fgy.bin
bool bootLoaderPraseFirmwarePath(const char *url, const uint16_t length)
{
    String urlPraseString;

    memset(otaParameter.firmwarePath, 0x00, sizeof(otaParameter.firmwareLength));

    for (uint16_t i=0; i<length; i++)
    {
        urlPraseString += (char)url[i];
    }

    if ((urlPraseString.indexOf(".bin") == -1)||(urlPraseString.indexOf("ota") == -1))
    {
        return false;
    }
    else
    {
        uint16_t position = urlPraseString.indexOf("/firmware");
        // logDebug("position = [%d]-[%d]-[%d]-[%d]", position,urlPraseString.substring(position).length(), length, urlPraseString.length());
        memcpy(otaParameter.firmwarePath, urlPraseString.substring(position).c_str(), urlPraseString.substring(position).length());

        return true;
    }
}
